/*
 *  Copyright 2012 yura.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */

package org.opu.odg.dg.xml;

import java.util.ArrayList;
import java.util.List;
import org.opu.odg.dg.xml.TextMarkupUtil.Lexem;
import org.opu.odg.dg.xml.TextMarkupUtil.LexemType;

/**
 *
 * @author yura
 */
public class ContentMapping {

    private ContentEntry current = null;
    private List<ContentEntry> list = new ArrayList<ContentEntry>();
    private List<String> varNameList = new ArrayList<String>();

    private XmlTag root;

    private List<Lexem> closeLexemWithoutOpen = new ArrayList<Lexem>();

    public void addContentEntry(TextElement txt) {
        for(Lexem l : txt.getLexems()) {
            switch (l.getType()) {
                case CONTENT_START:
                    openContentEntry(txt, l);
                    break;
                case CONTENT_END:
                    closeContentEntry(txt, l);
                    break;
                case VARIABLE:
                    if (current == null) {
                        varNameList.add(l.getText());
                    } else {
                        current.varNameList.add(l.getText());
                    }
                    break;
                default:
                    break;
            }
        }
    }

    public XmlTag getRoot() {
        return root;
    }

    public List<ContentEntry> getMappedEntry() {
        return list;
    }

    public List<String> getVarNameList() {
        return varNameList;
    }

    public void validate() {
        List<Lexem> notClosedLexems = new ArrayList<Lexem>();
        for (ContentEntry contentEntry : list) {
            validate(contentEntry, notClosedLexems);
        }

        if (notClosedLexems.isEmpty() && closeLexemWithoutOpen.isEmpty()){
            return;
        }

        throw new IllegalArgumentException("Not closed lexems - " + notClosedLexems
                + ", closed not opened lexem - " + closeLexemWithoutOpen);
    }

    void setRoot(XmlTag root) {
        this.root = root;
    }

    private void openContentEntry(TextElement txt, TextMarkupUtil.Lexem lexem) {
        ContentEntry entry = new ContentEntry(lexem.getText(),
                new LocalXpath(lexem, txt), current);

        if (current == null) {
            current = entry;
            list.add(current);
        } else {
            current.subContents.add(entry);
            current = entry;
        }
    }

    private void closeContentEntry(TextElement txt, TextMarkupUtil.Lexem lexem) {
        if (current == null) {
            closeTopLevelContent(txt, lexem);
            return;
        }

        if (current.name.equals(lexem.getText())) {
            current.setXpathEnd(new LocalXpath(lexem, txt));
            current = current.parent;
        } else {
            ContentEntry entry = current.parent;
            while(entry != null) {
                if (entry.name.equals(lexem.getText())) {
                    current = entry;
                    current.setXpathEnd(new LocalXpath(lexem, txt));
                    current = current.parent;
                    return;
                }
                entry = entry.parent;
            }
            closeTopLevelContent(txt, lexem);
        }
    }

    private void validate(ContentEntry entry, List<Lexem> notClosedLexems) {
        if (entry.getXpathEnd() == null) {
            notClosedLexems.add(entry.xpathStart.getLexem());
            for (ContentEntry contentEntry : entry.subContents) {
                validate(contentEntry, notClosedLexems);
            }
        }
    }

    private void closeTopLevelContent(TextElement txt, TextMarkupUtil.Lexem lexem) {
        for (ContentEntry entry : list) {
            if (entry.name.equals(lexem.getText())) {
                current = entry;
                current.setXpathEnd(new LocalXpath(lexem, txt));
                current = current.parent;
                return;
            }
        }
        closeLexemWithoutOpen.add(lexem);
    }

    public static class ContentEntry {
        public final String name;
        public final LocalXpath xpathStart;
        private LocalXpath xpathEnd;
        public final ContentEntry parent;
        public final List<ContentEntry> subContents = new ArrayList<ContentEntry>();
        public final List<String> varNameList = new ArrayList<String>();

        public ContentEntry(String name, LocalXpath xpathStart, ContentEntry parent) {
            this.name = name;
            this.xpathStart = xpathStart;
            this.parent = parent;
        }

        private void setXpathEnd(LocalXpath xpathEnd) {
            this.xpathEnd = xpathEnd;
        }

        public LocalXpath getXpathEnd() {
            return xpathEnd;
        }

        @Override
        public String toString() {
            return name;
        }
    }
}
